'************************************************************************************
' Copyright  2003-2005 Kathleen Dollard
'
' This software is provided 'as-is', without any express or implied warranty. In no 
' event will the author be held liable for any damages arising from the use of this
' software.
' 
' You may use this software for any purpose, including commercial applications, 
' and you may redistribute this software as part of application delivery: however, 
' this copyright notice may not be removed and you may not otherwise redistribute 
' this software directly other than providing a link to wwww.gendotnet.com. 
'
' You may alter this software to better server your needs (I'd like to hear about your 
' changes in order to make the software better). If you alter the softwre, you must  
' clearly mark it as altered and must not be misrepresented as the original software.
' nor wholly your original work.
'*************************************************************************************
'  Summary: Provides support for running XSLT templates
'  Refactor: There are too many translation methods

Option Strict On
Option Explicit On

Imports System
Imports System.Diagnostics

Namespace KADGen.CodeGenerationSupport
   '! Class Summary: 
   Public Enum XSLTParamSource
      Unknown
      Script
   End Enum

   Public Structure XSLTParam
      Public Name As String
      Public Value As Object
      Public Source As XSLTParamSource

      Public Sub New(ByVal Name As String, ByVal Value As Object, ByVal source As XSLTParamSource)
         Me.Source = source
         Me.Name = Name
         Me.Value = Value
      End Sub

      Public Sub New(ByVal Name As String, ByVal Value As Object)
         Me.Name = Name
         Me.Value = Value
      End Sub

      Public Sub New(ByVal Name As String)
         Me.Name = Name
      End Sub
   End Structure

   Public Structure ExtObject
      Public NameSpaceURI As String
      Public Value As Object
      Public Sub New(ByVal NameSpaceURI As String, ByVal Value As Object)
         Me.NameSpaceURI = NameSpaceURI
         Me.Value = Value
      End Sub
      Public Sub New(ByVal NameSpaceURI As String)
         Me.NameSpaceURI = NameSpaceURI
      End Sub
   End Structure

   Public Class XSLTSupport

#Region "Class level declarations - empty"
#End Region

#Region "Constructors - empty"
#End Region

#Region "Public Methods and Properties"
      Public Shared Sub GenerateViaXSLT( _
                  ByVal xsltFileName As String, _
                  ByVal MetaDataFileName As String, _
                  ByVal outputFile As String, _
                  ByVal outputType As String, _
                  ByVal extObjects() As ExtObject, _
                  ByVal ParamArray params() As XSLTParam)
         Dim xmlDoc As New Xml.XmlDocument
         xmlDoc.Load(MetaDataFileName)
         KADGen.LibraryInterop.Singletons.XMLDoc = xmlDoc
         KADGen.LibraryInterop.Singletons.NsMgr = KADGen.Utility.xmlHelpers.BuildNamespacesManagerForDoc(xmlDoc)
         GenerateViaXSLT(xsltFileName, xmlDoc, outputFile, outputType, extObjects, params)
      End Sub


      Public Shared Sub GenerateViaXSLT( _
                  ByVal xsltFileName As String, _
                  ByVal xmlMetaData As Xml.XmlDocument, _
                  ByVal outputFile As String, _
                  ByVal outputType As String, _
                   ByVal extObjects() As ExtObject, _
                  ByVal ParamArray params() As XSLTParam)
         Dim stream As IO.FileStream = Nothing
         Try
            stream = New IO.FileStream(outputFile, IO.FileMode.Create)
            GenerateXSLTToStream(xsltFileName, xmlMetaData, stream, outputType, extObjects, params)
         Finally
            If stream IsNot Nothing Then
               stream.Close()
            End If
         End Try

      End Sub

      Public Shared Function GenerateViaXSLT( _
                  ByVal xsltFileName As String, _
                  ByVal xmlMetaData As Xml.XmlDocument, _
                  ByVal outputType As String, _
                  ByVal extObjects() As ExtObject, _
                  ByVal ParamArray params() As XSLTParam) _
                  As IO.Stream
         Dim stream As IO.MemoryStream
         stream = New IO.MemoryStream
         GenerateXSLTToStream(xsltFileName, xmlMetaData, stream, outputType, extObjects, params)
         stream.Flush()
         stream.Seek(0, IO.SeekOrigin.Begin)
         Return stream
      End Function

      Public Shared Function GenerateViaXSLT( _
                  ByVal xslTransform As Xml.Xsl.XslCompiledTransform, _
                  ByVal xmlMetadata As Xml.XmlDocument, _
                  ByVal outputType As String, _
                  ByVal extObjects() As ExtObject, _
                  ByVal ParamArray params() As XSLTParam) _
                  As IO.Stream
         Dim stream As IO.MemoryStream
         stream = New IO.MemoryStream
         GenerateXSLTToStream(xslTransform, xmlMetadata, stream, outputType, extObjects, params)
         stream.Flush()
         stream.Seek(0, IO.SeekOrigin.Begin)
         Return stream
      End Function

      Public Shared Function GenerateViaXSLT( _
                  ByVal xsltFileName As String, _
                  ByVal MetaDataFileName As String, _
                  ByVal outputType As String, _
                  ByVal extObjects() As ExtObject, _
                  ByVal ParamArray params() As XSLTParam) _
                  As IO.Stream
         Dim xmlDoc As New Xml.XmlDocument
         xmlDoc.Load(MetaDataFileName)
         KADGen.LibraryInterop.Singletons.XMLDoc = xmlDoc
         KADGen.LibraryInterop.Singletons.NsMgr = KADGen.Utility.xmlHelpers.BuildNamespacesManagerForDoc(xmlDoc)
         Return GenerateViaXSLT(xsltFileName, xmlDoc, outputType, extObjects, params)
      End Function

      Public Shared Sub GenerateXMLViaXSLT( _
                  ByVal xsltFileName As String, _
                  ByVal xmlMetaData As Xml.XmlDocument, _
                  ByVal outputFile As String, _
                  ByVal outputType As String, _
                  ByVal extObjects() As ExtObject, _
                  ByVal ParamArray params() As XSLTParam)
         Dim xslt As New Xml.Xsl.XslCompiledTransform(True)
         Dim xNav As Xml.XPath.XPathNavigator
         Dim XMLWriter As Xml.XmlTextWriter = Nothing
         Dim args As New Xml.Xsl.XsltArgumentList
         Dim param As XSLTParam

         Try
            If xmlMetaData Is Nothing Then
               xmlMetaData = New Xml.XmlDocument
            End If

            For Each param In params
               args.AddParam(param.Name, "", param.Value)
            Next

            If extObjects Is Nothing Then
               ' No problem, just skip
            Else
               For Each extObject As extObject In extObjects
                  Dim constructorInfo As Reflection.ConstructorInfo = CType(extObject.Value, System.Type).GetConstructor(Nothing)
                  Dim obj As Object = constructorInfo.Invoke(Nothing)
                  args.AddExtensionObject(extObject.NameSpaceURI, obj)
               Next
            End If
            AddStandardExtension(args)

            xNav = xmlMetaData.CreateNavigator()
            XMLWriter = New Xml.XmlTextWriter(outputFile, Text.Encoding.UTF8)

            xslt.Load(xsltFileName)
            xslt.Transform(xNav, args, XMLWriter)
         Finally
            If Not XMLWriter Is Nothing Then
               XMLWriter.Flush()
               XMLWriter.Close()
            End If
         End Try

      End Sub

      Public Shared Function GetXSLTParamsFromStylesheet( _
               ByVal xsltFileName As String, _
               ByVal basePath As String, _
               ByVal ParamArray scriptParams() As XSLTParam) _
               As XSLTParam()
         Dim xsltStream As IO.Stream = Nothing
         Dim xmlInput As New Xml.XmlDocument
         Dim stream As New IO.MemoryStream
         Dim paramNames() As String
         Dim params() As XSLTParam

         Try
            xsltStream = Utility.Tools.GetStreamFromStringResource( _
                        GetType(XSLTSupport), "RetrieveParams.xslt")
            xmlInput.Load(xsltFileName)
            GenerateXSLTToStream(xsltStream, xmlInput, stream, "", Nothing)
            stream.Seek(0, IO.SeekOrigin.Begin)
            Dim reader As New IO.StreamReader(stream)
            paramNames = reader.ReadToEnd.Split("|"c)
            If paramNames.GetLength(0) = 1 AndAlso paramNames(0).Length = 0 Then
               ' there are no parameters
               ReDim params(0)
               params(0) = New XSLTParam("Name", "")
            Else
               ReDim params(paramNames.GetUpperBound(0))
               For i As Int32 = 0 To paramNames.GetUpperBound(0)
                  If paramNames(i).Trim.Length > 0 Then
                     params(i) = New XSLTParam(paramNames(i), "")
                     For j As Int32 = 0 To scriptParams.GetUpperBound(0)
                        If params(i).Name.ToLower = scriptParams(j).Name.ToLower Then
                           params(i).Source = XSLTParamSource.Script
                           params(i).Value = scriptParams(j).Value
                        End If
                     Next
                  End If
               Next
            End If
         Finally
            Try
               If stream IsNot Nothing Then
                  stream.Close()
               End If
            Catch
            End Try
            Try
               If xsltStream IsNot Nothing Then
                  xsltStream.Close()
               End If
            Catch
            End Try
         End Try
         Return params
      End Function



      Public Shared Sub FillXSLTParams( _
                     ByVal params() As XSLTParam, _
                     ByVal nav As Xml.XPath.XPathNavigator)
         For Each param As XSLTParam In params
            If Not param.Source = XSLTParamSource.Script Then
               param.Value = nav.GetAttribute(param.Name, "")
            End If
         Next
      End Sub


      Public Shared Function MergeExtendedAttributes(ByVal inputFileName As String) As IO.Stream
         Dim xsltStream As IO.Stream
         Dim xmlInput As New Xml.XmlDocument
         Dim stream As New IO.MemoryStream
         xsltStream = Utility.Tools.GetStreamFromStringResource( _
                       GetType(XSLTSupport), "AttributesFromExtendedProps.xslt")
         xmlInput.Load(inputFileName)
         GenerateXSLTToStream(xsltStream, xmlInput, stream, "", Nothing)
         Return stream
      End Function

      Public Shared Function GetXSLTParams(ByVal params As String) As CodeGenerationSupport.XSLTParam()
         Dim pairGroups() As String
         Dim pairs() As String
         Dim aList As New Collections.ArrayList
         pairGroups = params.Split("|"c)
         For Each group As String In pairGroups
            pairs = group.Split(":"c)
            If pairs.Length >= 2 Then
               Dim nvPair As New CodeGenerationSupport.XSLTParam(pairs(0), pairs(1), XSLTParamSource.Script)
               aList.Add(nvPair)
            End If
         Next
         Return CType(aList.ToArray(GetType(CodeGenerationSupport.XSLTParam)), CodeGenerationSupport.XSLTParam())
      End Function
#End Region

#Region "Protected and Friend Methods and Properties - empty"
#End Region

#Region "Private Methods and Properties"
      Private Shared Sub GenerateXSLTToStream( _
                  ByVal xsltFileName As String, _
                  ByVal xmlMetaData As Xml.XmlDocument, _
                  ByVal stream As IO.Stream, _
                  ByVal outputType As String, _
                  ByVal extObjects() As ExtObject, _
                  ByVal ParamArray params() As XSLTParam)
         Dim xslt As New Xml.Xsl.XslCompiledTransform(True)
         Dim streamWriter As New IO.StreamWriter(stream)
         Dim args As New Xml.Xsl.XsltArgumentList

         Try
            If xmlMetaData Is Nothing Then
               xmlMetaData = New Xml.XmlDocument
            End If

            xslt.Load(xsltFileName)
            GenerateXSLTToStream(xslt, xmlMetaData, stream, outputType, extObjects, params)
         Finally
            If Not streamWriter Is Nothing Then
               streamWriter.Flush()
            End If
         End Try
      End Sub

      Private Shared Sub GenerateXSLTToStream( _
               ByVal xsltStream As IO.Stream, _
               ByVal xmlMetaData As Xml.XmlDocument, _
               ByVal stream As IO.Stream, _
                  ByVal outputType As String, _
                ByVal extObjects() As ExtObject, _
               ByVal ParamArray params() As XSLTParam)
         Dim xslt As New Xml.Xsl.XslCompiledTransform(True)
         Dim xNav As Xml.XPath.XPathNavigator
         Dim streamWriter As New IO.StreamWriter(stream)
         Dim args As New Xml.Xsl.XsltArgumentList
         Dim xmlReader As Xml.XmlTextReader

         Try
            If xmlMetaData Is Nothing Then
               xmlMetaData = New Xml.XmlDocument
            End If

            xmlReader = New Xml.XmlTextReader(xsltStream)
            xslt.Load(xmlReader)
            GenerateXSLTToStream(xslt, xmlMetaData, stream, outputType, extObjects, params)
         Finally
            If Not streamWriter Is Nothing Then
               streamWriter.Flush()
            End If
         End Try
      End Sub

      Private Shared Sub GenerateXSLTToStream( _
                  ByVal xsltTransform As Xml.Xsl.XslCompiledTransform, _
                  ByVal xmlMetaData As Xml.XmlDocument, _
                  ByVal stream As IO.Stream, _
                  ByVal outputType As String, _
                  ByVal extObjects() As ExtObject, _
                  ByVal ParamArray params() As XSLTParam)
         Dim streamWriter As IO.StreamWriter = Nothing
         Dim xmlWriter As Xml.XmlTextWriter = Nothing
         Dim args As New Xml.Xsl.XsltArgumentList
         Dim param As XSLTParam

         Try
            If Not params Is Nothing Then
               For Each param In params
                  args.AddParam(param.Name, "", param.Value)
               Next
            End If

            If extObjects Is Nothing Then
               ' No problem, just skip
            Else
               For Each extObject As ExtObject In extObjects
                  Dim constructorInfo As Reflection.ConstructorInfo = CType(extObject.Value, System.Type).GetConstructor(Type.EmptyTypes)
                  Dim obj As Object = constructorInfo.Invoke(Nothing)
                  args.AddExtensionObject(extObject.NameSpaceURI, obj)
               Next
            End If
            AddStandardExtension(args)

            streamWriter = New IO.StreamWriter(stream)
            xsltTransform.Transform(New Xml.XmlNodeReader(xmlMetaData), args, streamWriter)
         Finally
            If Not streamWriter Is Nothing Then
               streamWriter.Flush()
            End If
            If Not xmlWriter Is Nothing Then
               xmlWriter.Flush()
            End If
         End Try
      End Sub

      'Private Shared Sub GenerateXSLTToStream( _
      '            ByVal xsltFileName As String, _
      '            ByVal xmlMetaData As Xml.XmlDocument, _
      '            ByVal stream As IO.Stream, _
      '            ByVal outputType As String, _
      '            ByVal extObjects() As ExtObject, _
      '            ByVal ParamArray params() As XSLTParam)
      '   Dim xslt As New Xml.Xsl.XslCompiledTransform
      '   Dim xNav As Xml.XPath.XPathNavigator
      '   Dim streamWriter As New IO.StreamWriter(stream)
      '   Dim args As New Xml.Xsl.XsltArgumentList

      '   Try
      '      If xmlMetaData Is Nothing Then
      '         xmlMetaData = New Xml.XmlDocument
      '      End If

      '      xNav = xmlMetaData.CreateNavigator()

      '      xslt.Load(xsltFileName)
      '      GenerateXSLTToStream(xslt, xNav, stream, outputType, extObjects, params)
      '   Finally
      '      If Not streamWriter Is Nothing Then
      '         streamWriter.Flush()
      '      End If
      '   End Try
      'End Sub

      'Private Shared Sub GenerateXSLTToStream( _
      '         ByVal xsltStream As IO.Stream, _
      '         ByVal xmlMetaData As Xml.XmlDocument, _
      '         ByVal stream As IO.Stream, _
      '            ByVal outputType As String, _
      '          ByVal extObjects() As ExtObject, _
      '         ByVal ParamArray params() As XSLTParam)
      '   Dim xslt As New Xml.Xsl.XslCompiledTransform
      '   Dim xNav As Xml.XPath.XPathNavigator
      '   Dim streamWriter As New IO.StreamWriter(stream)
      '   Dim args As New Xml.Xsl.XsltArgumentList
      '   Dim xmlReader As Xml.XmlTextReader

      '   Try
      '      If xmlMetaData Is Nothing Then
      '         xmlMetaData = New Xml.XmlDocument
      '      End If

      '      xNav = xmlMetaData.CreateNavigator()

      '      xmlReader = New Xml.XmlTextReader(xsltStream)
      '      xslt.Load(xmlReader)
      '      GenerateXSLTToStream(xslt, xNav, stream, outputType, extObjects, params)
      '   Finally
      '      If Not streamWriter Is Nothing Then
      '         streamWriter.Flush()
      '      End If
      '   End Try
      'End Sub

      'Private Shared Sub GenerateXSLTToStream( _
      '            ByVal xsltTransform As Xml.Xsl.XslCompiledTransform, _
      '            ByVal xNavMetaData As Xml.XPath.XPathNavigator, _
      '            ByVal stream As IO.Stream, _
      '            ByVal outputType As String, _
      '            ByVal extObjects() As ExtObject, _
      '            ByVal ParamArray params() As XSLTParam)
      '   Dim streamWriter As IO.StreamWriter = Nothing
      '   Dim xmlWriter As Xml.XmlTextWriter = Nothing
      '   Dim args As New Xml.Xsl.XsltArgumentList
      '   Dim param As XSLTParam

      '   Try
      '      If Not params Is Nothing Then
      '         For Each param In params
      '            args.AddParam(param.Name, "", param.Value)
      '         Next
      '      End If

      '      If extObjects Is Nothing Then
      '         ' No problem, just skip
      '      Else
      '         For Each extObject As ExtObject In extObjects
      '            Dim constructorInfo As Reflection.ConstructorInfo = CType(extObject.Value, System.Type).GetConstructor(Type.EmptyTypes)
      '            Dim obj As Object = constructorInfo.Invoke(Nothing)
      '            args.AddExtensionObject(extObject.NameSpaceURI, obj)
      '         Next
      '      End If
      '      AddStandardExtension(args)

      '      streamWriter = New IO.StreamWriter(stream)
      '      xsltTransform.Transform(xNavMetaData, args, streamWriter)
      '   Finally
      '      If Not streamWriter Is Nothing Then
      '         streamWriter.Flush()
      '      End If
      '      If Not xmlWriter Is Nothing Then
      '         xmlWriter.Flush()
      '      End If
      '   End Try
      'End Sub


      Private Shared Sub AddStandardExtension(ByVal args As Xml.Xsl.XsltArgumentList)
         args.AddExtensionObject("http://kadgen.com/StandardNETSupport.xsd", _
               New XSLTSupport.StandardSupport)

      End Sub

#End Region

      Private Class StandardSupport
         Private mIndent As Int32 = 2
         Private mIndentSize As Int32 = 2

         Public Sub SetIndentSize(ByVal value As Int32)
            mIndentSize = value
         End Sub

         Public Function GetIndent() As Int32
            Return mIndent
         End Function

         Public Sub SetIndent(ByVal Value As Int32)
            mIndent = Value
         End Sub

         Public Sub Indent()
            mIndent += 1
         End Sub

         Public Sub Outdent()
            mIndent -= 1
         End Sub

         Public Function InsertIndent() As String
            Dim sb As New Text.StringBuilder
            Dim repeatCount As Int32 = mIndentSize * mIndent
            If repeatCount > 0 Then
               sb.Append(" "c, mIndentSize * mIndent)
            End If
            Return sb.ToString
         End Function

         Public Function InsertNLIndent() As String
            Return Microsoft.VisualBasic.ControlChars.CrLf & InsertIndent()
         End Function
      End Class

   End Class
End Namespace